############################################################

class NumSyms:
  def __init__(i,nums,syms): 
    syms = updateSyms(syms)
    nums = updateNums(nums,Num(keep=True))
    entropy(syms)
    i.nums, i.syms, i.rank = nums,syms,0
  def __lt__(i1,i2):
    return i1.nums.mu < i2.nums.mu
  
def r(t,goal=lambda x:x.classes[0]):
  def q(q,a) : return a[int(0.01*q*len(a))][0]
  y = goal(t)
  for x in t.nums:
    todo = sorted([(r.cells[x.at],r.cells[y.at])
            for r in t.rows if r.cells[x.at] != The.skip])
    irq = q(75,todo) - q(25,todo)
    b   = int(math.ceil(2 * irq * len(todo)**(-1/3)))
    name = x.name
    all=[]
    for bin in bins(todo, b, lambda a,n:a[n][0]): 
      nums,classes=[],[]
      for num,klass in bin:
        nums    += [num]
        classes += [klass]
      all += [Some(nums=nums,classes=classes)]
    partition(all,HowEntropy())
    for x in all: print x

def Some(syms=[],nums=[],scores=[],classes=[]):
  return A(syms=syms,nums=nums,
           scores=scores,classes=classes,rank=0)

def partition(a,how,rank=1) :
  cut = split(a,how)
  print "C> ", cut
  if not cut:
    for x in a: x.rank = rank
  else:
    rank = partition(a[:cut], how, rank) + 1
    rank = partition(a[cut:], how, rank)
  return rank

def HowEntropy():
  return A(v      = lambda x  : x.e, 
           one    = Sym,
           get    = lambda x  : x.classes,
           init   = The.inf,
           add    = lambda x,y: addSyms(x,y),
           better = lambda x,y,u,r: x<y)

def HowVariance():
  return A(v      = lambda x  : x.scores, 
           one    = Num,
           get    = lambda x  : x.nums,
           init   = The.inf,
           add    = lambda x,y: addNums(x,y),
           better = lambda x,y,u,r: x<y)

def split(a,how):
  return split1(a,how,      None,      len(a) - 1, 
                  how.init, how.one(), how.one())

def split1(a,     how,      cut,       stop,      
                  best,     left,      right):
  def better(x,y): return how.better(x,y,left,right)
  def add(x,i)   : return how.add(x, how.get(a[i]))
  def v(x)       : return how.v(x)
  rights = {}
  for i in xrange(stop, 0, -1):
    rights[i] = right = add(right, i)
  print "STOP> ", stop - 1
  left = add(left,0)
  for i in xrange(1, stop - 1):
    right = rights[i+1]
    left  = add(left, i)
    here  = left.n * v(left) + right.n * v(right) 
    print "HB> ", here,best
    if better(here, best):
      cut , best = i , here
  return cut
    
def rX():
   r(rows('data/weather.csv'))
